import My3D.Point3D;
/* the 3D Matrix's structure
* [	[xx,yx,zx,wx]
* 	[xy,yy,zy,wy]
* 	[xz,yz,zz,wz]
* 	[xw,yw,zw,s ] ]
* */

dynamic class My3D.Matrix{
	public var matrixArray:Array;
	public static var say:Function;
	
	public function Matrix(obj:Object){
		this.matrixArray=new Array();
		if(obj==undefined){
			this.matrixArray=[	[1,0,0,0],
								[0,1,0,0],
								[0,0,1,0],
								[0,0,0,1]	];
		}
		if(obj[0][0]!=undefined){
			for(var i=0;i<obj.length;i++){
				this.matrixArray[i]=new Array();
				for(var ii=0;ii<obj[i].length;ii++){
					this.matrixArray[i][ii]=obj[i][ii];
				}
			}
		}
	}
	
	///Matrix 
	public function X(m2:Matrix):Matrix{
		if(m2==undefined)
		{
			return this;
		}
		if(this.matrixArray[0].length!= m2.matrixArray.length)
		{
			Say("the argument's structrue isn't match!");
			return null;
		}
		
		return Matrix.MatrixX(this,m2);	
	}
	
	public static function multiplyVector( m:Matrix, v:Point3D ):Void
	{
		var vx:Number, vy:Number, vz:Number;
		v.x = (vx=v.x) * m.matrixArray[0][0] + (vy=v.y) * m.matrixArray[0][1] + (vz=v.z) * m.matrixArray[0][2] + m.matrixArray[0][3];
		v.y = vx * m.matrixArray[1][0] + vy * m.matrixArray[1][1] + vz * m.matrixArray[1][2] + m.matrixArray[1][3];
		v.z = vx * m.matrixArray[2][0] + vy * m.matrixArray[2][1] + vz * m.matrixArray[2][2] + m.matrixArray[2][3]
	}
	public static function MatrixXStringCase(m1:Array, m2:Array):Array {
		if (m1 == undefined) {
			return m1;
		} else if (m2 == undefined) {
			return m2;
		}
		
		if (m1[0].length != m2.length) {
			say("the argument's structrue isn't match!");
			return null;
		}
		var rma:Array = new Array();
		for (var i = 0; i<m1.length; i++) {
			rma[i] = new Array();
			for (var ii = 0; ii<m2[0].length; ii++) {
				//var sum:Number = 0;
				var sum:String="";
				for (var iii = 0; iii<m1[0].length; iii++) {
					//sum += m1[i][iii]*m2[iii][ii];
					sum+="+"+m1[i][iii]+"*"+m2[iii][ii];
				}
				sum="("+sum.substring(1)+")";
				rma[i].push(sum);
			}
		}
		return rma;
	}
	public static function Matrix3X3(m1:Matrix, m2:Matrix):Matrix {
		return new Matrix([
		[m1.matrixArray[0][0] * m2.matrixArray[0][0] + m1.matrixArray[0][1] * m2.matrixArray[1][0] + m1.matrixArray[0][2] * m2.matrixArray[2][0]], [m1.matrixArray[0][0] * m2.matrixArray[0][1] + m1.matrixArray[0][1] * m2.matrixArray[1][1] + m1.matrixArray[0][2] * m2.matrixArray[2][1]], [m1.matrixArray[0][0] * m2.matrixArray[0][2] + m1.matrixArray[0][1] * m2.matrixArray[1][2] + m1.matrixArray[0][2] * m2.matrixArray[2][2]],
		[m1.matrixArray[1][0] * m2.matrixArray[0][0] + m1.matrixArray[1][1] * m2.matrixArray[1][0] + m1.matrixArray[1][2] * m2.matrixArray[2][0]], [m1.matrixArray[1][0] * m2.matrixArray[0][1] + m1.matrixArray[1][1] * m2.matrixArray[1][1] + m1.matrixArray[1][2] * m2.matrixArray[2][1]], [m1.matrixArray[1][0] * m2.matrixArray[0][2] + m1.matrixArray[1][1] * m2.matrixArray[1][2] + m1.matrixArray[1][2] * m2.matrixArray[2][2]],
		[m1.matrixArray[2][0] * m2.matrixArray[0][0] + m1.matrixArray[2][1] * m2.matrixArray[1][0] + m1.matrixArray[2][2] * m2.matrixArray[2][0]], [m1.matrixArray[2][0] * m2.matrixArray[0][1] + m1.matrixArray[2][1] * m2.matrixArray[1][1] + m1.matrixArray[2][2] * m2.matrixArray[2][1]], [m1.matrixArray[2][0] * m2.matrixArray[0][2] + m1.matrixArray[2][1] * m2.matrixArray[1][2] + m1.matrixArray[2][2] * m2.matrixArray[2][2]]
		
		]);
	}
	public static function Matrix4X4(m1:Matrix,m2:Matrix):Matrix {
		return new Matrix([
		[m1.matrixArray[0][0] * m2.matrixArray[0][0] + m1.matrixArray[0][1] * m2.matrixArray[1][0] + m1.matrixArray[0][2] * m2.matrixArray[2][0] + m1.matrixArray[0][3] * m2.matrixArray[3][0]], [m1.matrixArray[0][0] * m2.matrixArray[0][1] + m1.matrixArray[0][1] * m2.matrixArray[1][1] + m1.matrixArray[0][2] * m2.matrixArray[2][1] + m1.matrixArray[0][3] * m2.matrixArray[3][1]], [m1.matrixArray[0][0] * m2.matrixArray[0][2] + m1.matrixArray[0][1] * m2.matrixArray[1][2] + m1.matrixArray[0][2] * m2.matrixArray[2][2] + m1.matrixArray[0][3] * m2.matrixArray[3][2]], [m1.matrixArray[0][0] * m2.matrixArray[0][3] + m1.matrixArray[0][1] * m2.matrixArray[1][3] + m1.matrixArray[0][2] * m2.matrixArray[2][3] + m1.matrixArray[0][3] * m2.matrixArray[3][3]],
		[m1.matrixArray[1][0] * m2.matrixArray[0][0] + m1.matrixArray[1][1] * m2.matrixArray[1][0] + m1.matrixArray[1][2] * m2.matrixArray[2][0] + m1.matrixArray[1][3] * m2.matrixArray[3][0]], [m1.matrixArray[1][0] * m2.matrixArray[0][1] + m1.matrixArray[1][1] * m2.matrixArray[1][1] + m1.matrixArray[1][2] * m2.matrixArray[2][1] + m1.matrixArray[1][3] * m2.matrixArray[3][1]], [m1.matrixArray[1][0] * m2.matrixArray[0][2] + m1.matrixArray[1][1] * m2.matrixArray[1][2] + m1.matrixArray[1][2] * m2.matrixArray[2][2] + m1.matrixArray[1][3] * m2.matrixArray[3][2]], [m1.matrixArray[1][0] * m2.matrixArray[0][3] + m1.matrixArray[1][1] * m2.matrixArray[1][3] + m1.matrixArray[1][2] * m2.matrixArray[2][3] + m1.matrixArray[1][3] * m2.matrixArray[3][3]],
		[m1.matrixArray[2][0] * m2.matrixArray[0][0] + m1.matrixArray[2][1] * m2.matrixArray[1][0] + m1.matrixArray[2][2] * m2.matrixArray[2][0] + m1.matrixArray[2][3] * m2.matrixArray[3][0]], [m1.matrixArray[2][0] * m2.matrixArray[0][1] + m1.matrixArray[2][1] * m2.matrixArray[1][1] + m1.matrixArray[2][2] * m2.matrixArray[2][1] + m1.matrixArray[2][3] * m2.matrixArray[3][1]], [m1.matrixArray[2][0] * m2.matrixArray[0][2] + m1.matrixArray[2][1] * m2.matrixArray[1][2] + m1.matrixArray[2][2] * m2.matrixArray[2][2] + m1.matrixArray[2][3] * m2.matrixArray[3][2]], [m1.matrixArray[2][0] * m2.matrixArray[0][3] + m1.matrixArray[2][1] * m2.matrixArray[1][3] + m1.matrixArray[2][2] * m2.matrixArray[2][3] + m1.matrixArray[2][3] * m2.matrixArray[3][3]],
		[m1.matrixArray[3][0]*m2.matrixArray[0][0]+m1.matrixArray[3][1]*m2.matrixArray[1][0]+m1.matrixArray[3][2]*m2.matrixArray[2][0]+m1.matrixArray[3][3]*m2.matrixArray[3][0]],[m1.matrixArray[3][0]*m2.matrixArray[0][1]+m1.matrixArray[3][1]*m2.matrixArray[1][1]+m1.matrixArray[3][2]*m2.matrixArray[2][1]+m1.matrixArray[3][3]*m2.matrixArray[3][1]],[m1.matrixArray[3][0]*m2.matrixArray[0][2]+m1.matrixArray[3][1]*m2.matrixArray[1][2]+m1.matrixArray[3][2]*m2.matrixArray[2][2]+m1.matrixArray[3][3]*m2.matrixArray[3][2]],[m1.matrixArray[3][0]*m2.matrixArray[0][3]+m1.matrixArray[3][1]*m2.matrixArray[1][3]+m1.matrixArray[3][2]*m2.matrixArray[2][3]+m1.matrixArray[3][3]*m2.matrixArray[3][3]]
		])
	}
	public static function MatrixX(m1:Matrix,m2:Matrix):Matrix{
		if(m1==undefined )
		{
			return m1;
		}else if(m2==undefined){
			return m2;
		}
		
		if(m1.matrixArray[0].length!= m2.matrixArray.length)
		{
			say("the argument's structrue isn't match!");
			return null;
		}
		var rma:Array=new Array();
		for(var i=0;i<m1.matrixArray.length;i++){
			rma[i]=new Array();
			for(var ii=0;ii<m2.matrixArray[0].length;ii++){
				var sum:Number=0;
				for(var iii=0;iii<m1.matrixArray[0].length;iii++){
					sum+=m1.matrixArray[i][iii]*m2.matrixArray[iii][ii];
				}
				rma[i].push(sum);
			}
		}
		return new Matrix(rma);
	}
	///Matrix return the X-Axes Rotation transform Matrix
	public static function getXRotation(O:Number):Matrix{
		return new Matrix([
							[1, 0, 0, 0],
							[0, Math.cos(O), Math.sin(O), 0],
							[0, -Math.sin(O), Math.cos(O), 0],
							[0, 0, 0, 1]
							]);
	}
	///Matrix return the Y-Axes Rotation transform Matrix
	public static function getYRotation(O:Number):Matrix{
		return new Matrix([
							[Math.cos(O), 0, -Math.sin(O), 0],
							[0, 1, 0, 0],
							[Math.sin(O), 0, Math.cos(O), 0],
							[0, 0, 0, 1]
							]);
	}
	///Matrix return the Z-Axes Rotation transform Matrix
	public static function getZRotation(O:Number):Matrix{
		return new Matrix([
							[Math.cos(O), Math.sin(O), 0, 0],
							[ -Math.sin(O), Math.cos(O), 0, 0],
							[0, 0, 1, 0],
							[0, 0, 0, 1]
							]);
	}
	///Matrix return the 3-Axes Rotation transform Matrix
	public static function getFreeAxisRotation(x:Number,y:Number,z:Number,O:Number):Matrix{
		if(x==0 && y==0){
			return Matrix.getZRotation(O);
		}else if(x==0 && z==0){
			return Matrix.getYRotation(O);
		}else if(y==0 && z==0){
			return Matrix.getXRotation(O);
		}
		var a1:Number=Math.PI/2;
		var a2:Number=0;
		var y2:Number=Math.pow(y*y+z*z,0.5);
		if(z!=0){
			a1=Math.atan2(y,z);
			a2=-Math.atan2(x,y2);
		}else{
			a2=-Math.atan2(x,y2);
		}
		
		
		//trace(a1+" ||| "+a2);
		var m:Matrix=Matrix.getXRotation(a1);
		m=m.X(Matrix.getYRotation(a2));
		m=m.X(Matrix.getZRotation(O));
		m=m.X(Matrix.getYRotation(-a2));
		m=m.X(Matrix.getXRotation(-a1));
		return m;
	}
	///Matrix return movement transform
	public static function getMovement(x:Number,y:Number,z:Number):Matrix{
		return new Matrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [x, y, z, 1]]);
	}
	///Matrix return scalment transform
	public static function getScal(x:Number,y:Number,z:Number):Matrix{
		return new Matrix([[x, 0, 0, 0], [0, y, 0, 0], [0, 0, z, 0], [0,0,0, 1]]);
	}
	
	
	public function toString():String{
		var str:String="\n";
		for(var i=0;i<this.matrixArray.length;i++){
			str+=this.matrixArray[i].join(",")+"\n";
		}
		return str;
	}
	
	private function Say(o:Object){
		var msg:String="Matrix err: "+o.toString();
		if(Matrix.say!=undefined){
			Matrix.say(msg);
		}else{
			trace(msg);
		}
	}
}